package com.micro.utils;

import com.micro.entity.RequestHeader;
import com.micro.listener.HttpInfoListener;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Slf4j
public class HttpUtil {

    public static final String CONTENT_LENGTH = "Content-Length";
    private static OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), SSLSocketClient.getX509TrustManager())
            .hostnameVerifier(SSLSocketClient.getHostnameVerifier()).build();


    public static Response okHttpGet(String url, RequestHeader requestHeader) throws IOException {
        try {
            RequestHeader r = requestHeader == null || requestHeader.getHeaders() == null
                    ? new RequestHeader.Build().addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47").build() : requestHeader;
            Headers headers = Headers.of(r.getHeaders());
            Request request = new Request.Builder().url(url).headers(headers).get().build();
            Response response = okHttpClient.newBuilder().build().newCall(request).execute();
            return response;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * @param url
     * @param headers
     * @param path
     * @param limitSpeed
     * @param httpInfoListen
     * @throws IOException
     */
    public static void httpGetDownload(String url, Map<String, String> headers, String path, int limitSpeed, HttpInfoListener httpInfoListen) throws IOException {
        HttpURLConnection conn = getHttpURLConnection(url, "GET", headers);
        conn.connect();
        httpInfoListen.setContentLength(Long.parseLong(conn.getHeaderField(CONTENT_LENGTH)));
        writeOutFile(conn.getInputStream(), path, limitSpeed, httpInfoListen);
        conn.disconnect();
    }

    /**
     * @param url
     * @param headers
     * @return
     * @throws IOException
     */
    public static byte[] httpGetBytes(String url, Map<String, String> headers) throws IOException {
        HttpURLConnection connection = getHttpURLConnection(url, null, headers);
        connection.connect();
        byte[] bytes = ByteUtil.getBytes(connection.getInputStream());
        connection.disconnect();
        return bytes;
    }

    public static void main(String[] args) throws IOException {
        HttpsURLConnection httpsURLConnection = (HttpsURLConnection) new URL("https://dash.madou.club/videos/618a02513bc0ca4833b96d87/index.m3u8?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3MiOiJ2aWV3IiwiaWF0IjoxNjQ1MjUzMzI2LCJleHAiOjE2NDUyNTM0MjZ9.7PTeLepzMGsObXaJxYTygw0DrT_AMJG41jYhv74UFmI").openConnection();
        httpsURLConnection.setRequestMethod("GET");
        httpsURLConnection.setSSLSocketFactory(SSLSocketClient.getSSLSocketFactory());
        httpsURLConnection.setRequestProperty("referer","https://dash.madou.club/share/618a02513bc0ca4833b96d87");
        httpsURLConnection.connect();
        InputStream inputStream = httpsURLConnection.getInputStream();
        log.info(new String(writeToBytes(inputStream, 0, null)));
    }

    /**
     * @param url
     * @param headers
     * @return
     * @throws IOException
     */
    public static String httpGetString(String url, Map<String, String> headers) throws IOException {
        return new String(httpGetBytes(url, headers), StandardCharsets.UTF_8);
    }


    @NotNull
    private static HttpURLConnection getHttpURLConnection(String url, String method, Map<String, String> headers) throws IOException {
        URL u = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) u.openConnection();
        conn.setRequestMethod(method == null || "".equals(method) ? "GET" : method);
        if (headers != null) {
            headers.forEach(conn::setRequestProperty);
        }
        conn.setConnectTimeout(12000);
        conn.setReadTimeout(12000);
        return conn;
    }

    private static HttpsURLConnection getHttpsURLConnection(String url, String method, Map<String, String> headers) throws IOException {
        URL u = new URL(url);
        HttpsURLConnection conn = (HttpsURLConnection) u.openConnection();
        conn.setRequestMethod(method == null || "".equals(method) ? "GET" : method);
        conn.setSSLSocketFactory(SSLSocketClient.getSSLSocketFactory());
        if (headers != null) {
            headers.forEach(conn::setRequestProperty);
        }
        conn.setConnectTimeout(12000);
        conn.setReadTimeout(12000);
        return conn;
    }

    public static void httpGetDownload(String url, Map<String, String> headers, int limitSpeed, HttpInfoListener httpInfoListen) throws IOException {
        try {
            HttpURLConnection httpConn = getHttpURLConnection(url, "GET", headers);
            httpConn.connect();
            httpInfoListen.setContentLength(Long.parseLong(httpConn.getHeaderField(CONTENT_LENGTH)));
            byte[] bytes = writeToBytes(httpConn.getInputStream(), limitSpeed, httpInfoListen);
            httpInfoListen.setBytes(bytes);
            httpConn.disconnect();
        } catch (Exception e) {
            HttpsURLConnection httpsConn = getHttpsURLConnection(url, "GET", headers);
            httpsConn.connect();
            httpInfoListen.setContentLength(Long.parseLong(httpsConn.getHeaderField(CONTENT_LENGTH)));
            byte[] bytes = writeToBytes(httpsConn.getInputStream(), limitSpeed, httpInfoListen);
            httpInfoListen.setBytes(bytes);
        }

    }

    public static void httpGetContentLength(String url, Map<String, String> headers) {

    }

    public static byte[] writeToBytes(InputStream inputStream, int limitSpeed, HttpInfoListener httpInfoListen) {
        byte[] bytes = new byte[1024];
        int length = 0;
        int speed = 0;
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            long startTime = 0;
            long countLength = 0;
            while ((length = inputStream.read(bytes)) != -1) {
                if (startTime == 0) {
                    startTime = System.currentTimeMillis();
                }
                bos.write(bytes, 0, length);
                bos.flush();
                countLength = countLength + length;
                if (limitSpeed > 0) {
                    if (countLength >= limitSpeed * 1024) {
                        httpInfoListen.setSpeed(Long.valueOf(countLength) / 1024);
                        TimeUnit.MILLISECONDS.sleep(1000);
                        countLength = 0;
                    } else if (System.currentTimeMillis() - startTime >= 1000) {
                        httpInfoListen.setSpeed(Long.valueOf(countLength) / 1024);
                        countLength = 0;
                        startTime = 0;
                    }
                }
                if (System.currentTimeMillis() - startTime >= 1000) {
                    httpInfoListen.setSpeed(Long.valueOf(countLength) / 1024);
                    countLength = 0;
                    startTime = 0;
                }
            }
            return bos.toByteArray();
        } catch (InterruptedException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void writeOutFile(InputStream inputStream, String path, int limitSpeed, HttpInfoListener httpInfoListen) throws IOException {
        byte[] bytes = new byte[1024];
        int length = 0;
        int speed = 0;
        File file = new File(path);
        file.getParentFile().mkdirs();
        try (FileOutputStream fos = new FileOutputStream(path)) {
            long startTime = 0;
            long countLength = 0;
            while ((length = inputStream.read(bytes)) != -1) {
                startTime = System.nanoTime();
                fos.write(bytes, 0, length);
                fos.flush();
                countLength = countLength + length;
                if (limitSpeed > 0) {
                    if (countLength >= limitSpeed * 1024) {
                        httpInfoListen.setSpeed(Long.valueOf(countLength) / 1024);
                        TimeUnit.MILLISECONDS.sleep(1000);
                        countLength = 0;
                    } else if (System.nanoTime() - startTime >= 1000000000) {
                        httpInfoListen.setSpeed(Long.valueOf(countLength) / 1024);
                        countLength = 0;
                        startTime = 0;
                    }
                } else if (System.nanoTime() - startTime >= 1000000000) {
                    httpInfoListen.setSpeed(Long.valueOf(countLength) / 1024);
                    countLength = 0;
                    startTime = 0;
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class SSLSocketClient {

    public static final String SSL = "SSL";

    //获取这个SSLSocketFactory
    public static SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance(SSL);
            sslContext.init(null, getTrustManager(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //获取TrustManager
    private static TrustManager[] getTrustManager() {
        return new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
        };
    }

    //获取HostnameVerifier
    public static HostnameVerifier getHostnameVerifier() {
        return (s, sslSession) -> true;
    }

    public static X509TrustManager getX509TrustManager() {
        X509TrustManager trustManager = null;
        try {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore) null);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
            }
            trustManager = (X509TrustManager) trustManagers[0];
        } catch (Exception e) {
            e.printStackTrace();
        }

        return trustManager;
    }
}

